#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>

#pragma pack(push, 1)

typedef struct
{
    char name[100];      // 文件名
    uint64_t size;       // 文件大小
} file_info;

#pragma pack(pop)

// 获取文件大小
uint64_t get_file_size(const char* filename) {
    struct stat st;
    if(stat(filename, &st) == 0) {
        return st.st_size;
    }
    return 0;
}

// 发送单个文件
int send_file(int sock, const char* filename) {
    file_info fi;
    
    // 填充文件信息结构体
    strncpy(fi.name, filename, sizeof(fi.name) - 1);
    fi.name[sizeof(fi.name) - 1] = '\0';
    fi.size = get_file_size(filename);
    
    if(fi.size == 0) {
        printf("文件不存在或为空: %s\n", filename);
        return -1;
    }
    
    // 发送文件信息
    if(send(sock, &fi, sizeof(fi), 0) != sizeof(fi)) {
        perror("send file_info");
        return -1;
    }
    
    // 打开文件
    int fd = open(filename, O_RDONLY);
    if(fd == -1) {
        perror("open file");
        return -1;
    }
    
    // 发送文件数据
    char buffer[4096];
    uint64_t remaining = fi.size;
    uint64_t total_sent = 0;
    ssize_t bytes_read, bytes_sent;
    
    printf("正在发送文件: %s (大小: %lu bytes)\n", filename, fi.size);
    
    while(remaining > 0) {
        // 读取文件数据
        bytes_read = read(fd, buffer, sizeof(buffer));
        if(bytes_read <= 0) {
            if(bytes_read == -1) perror("read file");
            break;
        }
        
        // 发送数据
        bytes_sent = send(sock, buffer, bytes_read, 0);
        if(bytes_sent <= 0) {
            if(bytes_sent == -1) perror("send file data");
            break;
        }
        
        total_sent += bytes_sent;
        remaining -= bytes_sent;
        
        // 显示进度
        int progress = (int)((double)total_sent / fi.size * 100);
        printf("\r进度: %d%% (%lu/%lu bytes)", progress, total_sent, fi.size);
        fflush(stdout);
    }
    
    printf("\n");
    close(fd);
    
    if(total_sent == fi.size) {
        printf("文件发送成功: %s\n", filename);
        return 0;
    } else {
        fprintf(stderr, "文件发送不完整: %s (%lu/%lu bytes)\n", 
                filename, total_sent, fi.size);
        return -1;
    }
}

// 发送目录中的所有文件
int send_directory(int sock, const char* dirpath) {
    DIR* dir = opendir(dirpath);
    if(!dir) {
        perror("opendir");
        return -1;
    }
    
    struct dirent* entry;
    int file_count = 0;
    char filepath[512];
    
    while((entry = readdir(dir)) != NULL) {
        // 跳过 . 和 .. 目录
        if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }
        
        // 构建完整文件路径
        snprintf(filepath, sizeof(filepath), "%s/%s", dirpath, entry->d_name);
        
        // 检查是否为普通文件
        struct stat st;
        if(stat(filepath, &st) == 0 && S_ISREG(st.st_mode)) {
            if(send_file(sock, filepath) == 0) {
                file_count++;
            }
        }
    }
    
    closedir(dir);
    return file_count;
}

int main(int argc, char** argv)
{
    if(argc < 3) {
        fprintf(stderr, "Usage: %s <port> <file1> [file2] ...\n", argv[0]);
        fprintf(stderr, "       %s <port> -d <directory>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int server_port = atoi(argv[1]);
    
    // 忽略 SIGPIPE 信号
    signal(SIGPIPE, SIG_IGN);

    // 第1步：创建监听套接字
    int sock_listen = socket(AF_INET, SOCK_STREAM, 0);
    if(sock_listen == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 开启地址复用，以便服务器快速重启
    int opt_val = 1;
    setsockopt(sock_listen, SOL_SOCKET, SO_REUSEADDR, &opt_val, sizeof(opt_val));

    // 第2步：绑定地址
    struct sockaddr_in myaddr;
    myaddr.sin_family = AF_INET;
    myaddr.sin_addr.s_addr = INADDR_ANY;
    myaddr.sin_port = htons(server_port);

    if(bind(sock_listen, (struct sockaddr*)&myaddr, sizeof(myaddr)) == -1) {
        perror("bind");
        close(sock_listen);
        exit(EXIT_FAILURE);
    }

    // 第3步：监听
    if(listen(sock_listen, 5) == -1) {
        perror("listen");
        close(sock_listen);
        exit(EXIT_FAILURE);
    }

    printf("文件服务器启动成功，监听端口 %d\n", server_port);
    printf("等待客户端连接...\n");

    // 第4步：接受客户端连接
    struct sockaddr_in client_addr;
    socklen_t addr_len = sizeof(client_addr);
    int sock_client = accept(sock_listen, (struct sockaddr*)&client_addr, &addr_len);
    
    if(sock_client == -1) {
        perror("accept");
        close(sock_listen);
        exit(EXIT_FAILURE);
    }

    printf("客户端(%s:%hu)已连接\n", 
           inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
    printf("开始发送文件...\n\n");

    int total_files = 0;
    uint64_t total_sent = 0;

    // 处理文件发送
    if(argc == 4 && strcmp(argv[2], "-d") == 0) {
        // 发送整个目录
        printf("发送目录: %s\n", argv[3]);
        total_files = send_directory(sock_client, argv[3]);
    } else {
        // 发送指定文件
        for(int i = 2; i < argc; i++) {
            if(send_file(sock_client, argv[i]) == 0) {
                total_files++;
                total_sent += get_file_size(argv[i]);
            }
        }
    }

    printf("\n文件传输统计:\n");
    printf("成功发送文件数量: %d\n", total_files);
    printf("总发送数据量: %lu bytes\n", total_sent);

    // 关闭连接
    close(sock_client);
    close(sock_listen);

    printf("文件传输完成，服务器关闭\n");
    return 0;
}
